/*
 *  app_led.c -- provide ISSI IS31FL3216 led driver chip operation interface.
 *
 *  ORIGINAL AUTHOR: Xu Chun (chun.xu@milliwave.cn)
 */

#include "led_drive.h"
#include "mdev_i2c.h"
#include "wm_os.h"

#define _nop_()     asm("nop");\
	asm("nop");\
asm("nop");\
asm("nop");\
asm("nop");\
asm("nop");\
asm("nop");\
asm("nop");

#define DEFAULT_NOP_CNT 1

static const uint8_t led_gamma32[] = {
	0, 1, 2, 4, 6, 10, 13, 18, 22, 28, 33, 39,
	46, 53, 61, 69, 78, 86, 96, 106, 116, 126,
	138, 149, 161, 173, 186, 199, 212, 226, 240, 255
};

static const uint8_t led_gamma64[] = {
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18,
	20, 22, 24, 26, 29, 32, 35, 38, 41, 44, 47, 50,
	53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 94, 99,
	104, 109, 114, 119, 124, 129, 134, 140, 146, 152, 
	158, 164, 170, 176, 182, 188, 195, 202, 209, 216,
	223, 230, 237, 244, 251, 255
};

static void chip_power(int status)
{
	static uint8_t init = 0;

	if(init == 0)
	{
		PMU_PowerOnVDDIO(PMU_VDDIO_D1);
		GPIO_PinMuxFun(CHIP_POWER_EN, PINMUX_FUNCTION_0);
		GPIO_SetPinDir(CHIP_POWER_EN, GPIO_OUTPUT);
		init = 1;
	}
	GPIO_WritePinOutput(CHIP_POWER_EN, !!status);
}

static void inline chip_iic_delay(int32_t cnt)
{
	while(cnt--)
		_nop_();
}

static void chip_i2c_init(void)
{
	enable_iic_power();
	enable_scl_pin();
	enable_sda_pin();
	enable_scl_pullup();
	enable_sda_pullup();
	set_scl_output();
	set_sda_output();
	scl_high();
	sda_high();
	chip_iic_delay(DEFAULT_NOP_CNT);
}

static void chip_i2c_start(void)
{
	sda_high();
	chip_iic_delay(DEFAULT_NOP_CNT);
	scl_high();
	chip_iic_delay(DEFAULT_NOP_CNT);
	sda_low();
	chip_iic_delay(DEFAULT_NOP_CNT);
	scl_low();
	chip_iic_delay(DEFAULT_NOP_CNT);
}

static void chip_i2c_stop(void)
{
	scl_low();
	chip_iic_delay(DEFAULT_NOP_CNT);
	sda_low();
	chip_iic_delay(DEFAULT_NOP_CNT);
	scl_high();
	chip_iic_delay(DEFAULT_NOP_CNT);
	sda_high();
	chip_iic_delay(DEFAULT_NOP_CNT);
}

static I2cAck chip_i2c_recvack(void)
{
	uint8_t errCounts = 255;
	set_sda_input();
	chip_iic_delay(DEFAULT_NOP_CNT);
	scl_high();
	chip_iic_delay(DEFAULT_NOP_CNT);
	while(get_sda_state())
	{
		if(errCounts == 0)
		{
			set_sda_output();
			chip_i2c_stop();
			return NO_ACK;
		}
		errCounts--; 
	}
	scl_low();
	chip_iic_delay(DEFAULT_NOP_CNT);
	set_sda_output();
	return ACK;
}

static int chip_i2c_writebyte(const uint8_t data)
{
	uint8_t bit_cnt;
	for(bit_cnt = 0; bit_cnt < 8; ++bit_cnt)
	{
		scl_low();
		chip_iic_delay(DEFAULT_NOP_CNT);
		if(((data << bit_cnt) & 0x80) == 0)
			sda_low();
		else
			sda_high();
		chip_iic_delay(DEFAULT_NOP_CNT);
		scl_high();
		chip_iic_delay(DEFAULT_NOP_CNT);
	}
	scl_low();
	chip_iic_delay(DEFAULT_NOP_CNT);
	sda_high();
	chip_iic_delay(DEFAULT_NOP_CNT);
	if(chip_i2c_recvack() == NO_ACK)
		return -NO_ACK;
	else
		return ACK;
}

#if 0
static void chip_i2c_sendack(I2cAck ack) 
{
	set_sda_output();
	scl_low();
	chip_iic_delay(DEFAULT_NOP_CNT);
	if(chip_i2c_recvack() == ACK)
		sda_low();
	else
		sda_high();
	chip_iic_delay(DEFAULT_NOP_CNT);
	scl_high();
	chip_iic_delay(DEFAULT_NOP_CNT);
	scl_low();
	chip_iic_delay(DEFAULT_NOP_CNT);
}

static uint8_t chip_i2c_readbyte(I2cAck ack)
{
	uint8_t data = 0, bit_cnt = 8;
	sda_high();
	chip_iic_delay(DEFAULT_NOP_CNT);
	set_sda_input();
	chip_iic_delay(DEFAULT_NOP_CNT);
	while(bit_cnt--)
	{
		data <<= 1;
		scl_low();
		chip_iic_delay(DEFAULT_NOP_CNT);
		scl_high();
		chip_iic_delay(DEFAULT_NOP_CNT);
		if(get_sda_state())
			data |= 0x01;
	}
	scl_low();
	chip_iic_delay(DEFAULT_NOP_CNT);
	chip_i2c_sendack(ack);
	return data;
}
#endif

int led_chip_current_config(led_current_e current)
{
	int ret;

	chip_i2c_start();
	ret = chip_i2c_writebyte(CHIP_I2C_BASE_ADDR | CHIP_AD_SEL0 | CHIP_WRITE);
	if(ret < 0)
		return -1;

	ret = chip_i2c_writebyte(CHIP_OUTPUT_CURRENT);
	if(ret < 0)
		return -1;
	ret = chip_i2c_writebyte(current << 4);
	if(ret < 0)
		return -1;
	chip_i2c_stop();

	return 0;
}

int led_chip_channel_config(led_channel_mode_e channel)
{
	int ret;

	chip_i2c_start();
	ret = chip_i2c_writebyte(CHIP_I2C_BASE_ADDR | CHIP_AD_SEL0 | CHIP_WRITE);
	if(ret < 0)
		return -1;
	ret = chip_i2c_writebyte(CHIP_CHANNEL_CONFIG);
	if(ret < 0)
		return -1;
	ret = chip_i2c_writebyte(channel);
	if(ret < 0)
		return -1;
	chip_i2c_stop();

	return 0;
}

int led_chip_pwm_config(led_channel_e channel, uint8_t intensity, uint8_t count)
{
	int ret, i;

	/* Config */
	chip_i2c_start();
	ret = chip_i2c_writebyte(CHIP_I2C_BASE_ADDR | CHIP_AD_SEL0 | CHIP_WRITE);
	if(ret < 0)
		return -1;
	ret = chip_i2c_writebyte(channel);
	if(ret < 0)
		return -1;
	for(i = 0; i < count; i++)
	{
		ret = chip_i2c_writebyte(intensity);  /* 0- 255 steps */
		if(ret < 0)
			return -1;
	}
	chip_i2c_stop();

	/* Update */
	chip_i2c_start();
	ret = chip_i2c_writebyte(CHIP_I2C_BASE_ADDR | CHIP_AD_SEL0 | CHIP_WRITE);
	if(ret < 0)
		return -1;
	ret = chip_i2c_writebyte(CHIP_PWM_UPDATE);
	if(ret < 0)
		return -1;
	ret = chip_i2c_writebyte(0x00);  
	if(ret < 0)
		return -1;
	chip_i2c_stop();

	return 0;
}

int led_chip_init(void)
{
	int ret;

	/* Chip power up and i2c interface init */
	chip_power(1);
	chip_i2c_init();
	chip_i2c_start();

	/* Chip config */
	ret = chip_i2c_writebyte(CHIP_I2C_BASE_ADDR | CHIP_AD_SEL0 | CHIP_WRITE);
	if(ret < 0)
		return -1;
	ret = chip_i2c_writebyte(CHIP_CONFIG);
	if(ret < 0)
		return -1;
	ret = chip_i2c_writebyte(0x00);
	if(ret < 0)
		return -1;
	chip_i2c_stop();

	wmprintf("led chip init success.\r\n");

	return 0;
}

static void led_step_update(uint8_t *step, uint8_t *flag)
{
	if((*flag) == 0)
		(*step) = (*step) + 1;
	if((*step) > 31)
		(*step) = 32;
	if((*flag) == 1 || (*step) == 32)
	{
		(*flag) = 1;
		if((*step) == 32)
			(*step) = 31;
		(*step) = (*step) - 1;
		if((*step) < 0)
			(*step) = 0;
	}
	if((*step) == 0)
		(*flag) = 0;
}

/* Breathing test */
int led_chip_test1(void)
{   
	int ret, intensity = 0, flag = 0;

	ret = led_chip_init();
	if(ret < 0)
		return -1;

	wmprintf("led chip output current config.\r\n");
	ret = led_chip_current_config(ILEDX1_0);
	if(ret < 0)
		return -1;
	ret = led_chip_channel_config(LEDOUTPUT);
	if(ret < 0)
		return -1;

	wmprintf("led chip pwm output.\r\n");

	for(;;)
	{
		led_chip_pwm_config(OUTPUT16, led_gamma32[intensity], 16);
		if(flag == 0)
			intensity = intensity + 1;
		if(intensity > 31)
			intensity = 32;
		if(flag == 1 || intensity == 32)
		{
			flag = 1;
			if(intensity == 32)
				intensity = 31;
			intensity = intensity - 1;
			if(intensity < 0)
				intensity = 0;
		}
		if(intensity == 0)
			flag = 0;
		os_thread_sleep(os_msec_to_ticks(30));
	}

	return 0;
}

/* Breathing and running water light test */
int led_chip_test2(void)
{
	int ret;
	uint8_t i, step[5], flag[5];
	uint8_t cycle = 0, start[5];

	ret = led_chip_init();
	if(ret < 0)
		return -1;

	wmprintf("led chip output current config.\r\n");
	ret = led_chip_current_config(ILEDX1_0);
	if(ret < 0)
		return -1;
	ret = led_chip_channel_config(LEDOUTPUT);
	if(ret < 0)
		return -1;

	wmprintf("led chip pwm output.\r\n");

	/* Step init */
	for(i = 0; i < sizeof(step); i++)
	{
		step[i] = 0;
		flag[i] = 0;
		start[i] = 0;
	}
	start[0] = 1;

	for(;;)
	{
		/* 1way */
		if(start[0] == 1)
		{
			led_chip_pwm_config(OUTPUT3, led_gamma32[step[0]], 1);
			led_step_update(&step[0], &flag[0]);
			if(flag[0] == 1 && cycle == 0)
			{
				cycle = 1;
				chip_iic_delay(500);
			}
			else if(cycle == 1 && flag[0] == 0)
			{
				cycle = 0;
				start[0] = 0;
				led_chip_pwm_config(OUTPUT3, led_gamma32[step[0]], 1);
				start[1] = 1;
			}
		}

		chip_iic_delay(500);

		/* 2way */
		if(start[1] == 1)
		{
			led_chip_pwm_config(OUTPUT6, led_gamma32[step[1]], 1);
			led_step_update(&step[1], &flag[1]);
			if(flag[1] == 1 && cycle == 0)
			{
				cycle = 1;
				chip_iic_delay(500);
			}
			else if(cycle == 1 && flag[1] == 0)
			{
				cycle = 0;
				start[0] = 0;
				start[1] = 0;
				led_chip_pwm_config(OUTPUT6, led_gamma32[step[1]], 1);
				start[2] = 1;
			}
		}

		chip_iic_delay(500);

		/* 3way */
		if(start[2] == 1)
		{
			led_chip_pwm_config(OUTPUT9, led_gamma32[step[2]], 1);
			led_step_update(&step[2], &flag[2]);
			if(flag[2] == 1 && cycle == 0)
			{
				cycle = 1;
				chip_iic_delay(500);
			}
			else if(cycle == 1 && flag[2] == 0)
			{
				cycle = 0;
				start[0] = 0;
				start[1] = 0;
				start[2] = 0;
				led_chip_pwm_config(OUTPUT9, led_gamma32[step[2]], 1);
				start[3] = 1;
			}
		}

		chip_iic_delay(500);

		/* 4 way */
		if(start[3] == 1)
		{
			led_chip_pwm_config(OUTPUT15, led_gamma32[step[3]], 1);
			led_step_update(&step[3], &flag[3]);
			if(flag[3] == 1 && cycle == 0)
			{
				cycle = 1;
				chip_iic_delay(500);
			}
			else if(cycle == 1 && flag[3] == 0)
			{
				cycle = 0;
				start[0] = 0;
				start[1] = 0;
				start[2] = 0;
				start[3] = 0;
				led_chip_pwm_config(OUTPUT15, led_gamma32[step[3]], 1);
				start[4] = 1;
			}
		}

		chip_iic_delay(500);

		/* 5 way */
		if(start[4] == 1)
		{
			led_chip_pwm_config(OUTPUT12, led_gamma32[step[4]], 1);
			led_step_update(&step[4], &flag[4]);
			if(flag[4] == 1 && cycle == 0)
			{
				cycle = 1;
				chip_iic_delay(500);
			}
			else if(cycle == 1 && flag[4] == 0)
			{
				cycle = 0;
				start[0] = 0;
				start[1] = 0;
				start[2] = 0;
				start[3] = 0;
				start[4] = 0;
				led_chip_pwm_config(OUTPUT12, led_gamma32[step[4]], 1);
				start[0] = 1;
			}
		}
	}
}
